//*****************************************************************************
//
// Copyright, (C) 2000-2005 Quantic Dream SA
//
// These coded instructions, statements and computer programs contain
// unpublished information, proprietary to Quantic Dream SA and are 
// protected by French and EEC copyright laws. They may not be 
// disclosed to third parties or copied or duplicated, in whole or in part, 
// without prior written consent of Quantic Dream SA
//
// Unpublished-rights reserved under the Copyright Laws of the EEC.
//
//*****************************************************************************
//
//	CLASS:	MESH
//
//	JJA : 2012-07-23 - Created
//*****************************************************************************

#include	"Mesh.h"

#include	<stdlib.h>

#include	<QDataStream>
#include	<QFile>
#include	<QTextStream>
#include	<QStringList>
#include	<QMessageBox>
#include	<math.h>

//=============================================================================
//	CODE STARTS HERE
//=============================================================================

//-----------------------------------------------------------------------------
//! @brief		MESH constructor
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-23
//-----------------------------------------------------------------------------
MESH::MESH()
: _uiVertexArrayId		( GL_INVALID_INDEX )
, _uiVertexBufferId		( GL_INVALID_INDEX )
//, _uiColorBufferId		( GL_INVALID_INDEX )
, _uiElementArrayId		( GL_INVALID_INDEX )
, _uiVerticesCount		( 0 )
, _uiElementsCount		( 0 )
, _pcMeshData			( NULL )
, _puiElementIndices	( NULL )
{
	
}

//-----------------------------------------------------------------------------
//! @brief		MESH destructor
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-23
//-----------------------------------------------------------------------------
MESH::~MESH()
{
	if	( _pcMeshData )
	{
		delete[] _pcMeshData;
	}

	if	( _puiElementIndices )
	{
		delete[] _puiElementIndices;
	}
}

//-----------------------------------------------------------------------------
//! @brief		Load
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	MESH::Load(const QByteArray *	pMeshData )
{
	QDataStream		MeshDataStream( *pMeshData );
	const char*		pCurrentData = pMeshData->constData();

	MeshDataStream.setByteOrder( QDataStream::LittleEndian );
	MeshDataStream.setFloatingPointPrecision( QDataStream::SinglePrecision );

	MeshDataStream >> _uiVerticesCount;
	pCurrentData += sizeof(int);

	uint uiColorDataOffset = 4 * sizeof(float);

	glGenVertexArrays( 1, &_uiVertexArrayId );
	glBindVertexArray( _uiVertexArrayId );
	
	glGenBuffers( 1, &_uiVertexBufferId );
	glBindBuffer( GL_ARRAY_BUFFER, _uiVertexBufferId );
	
	glBufferData( GL_ARRAY_BUFFER, _uiVerticesCount * 8 * sizeof(float), pCurrentData, GL_STATIC_DRAW );

	glVertexAttribPointer( VERTEX_ATTRIB_POSITION, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0 );
	glVertexAttribPointer( VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)uiColorDataOffset );

	glEnableVertexAttribArray( VERTEX_ATTRIB_POSITION );
	glEnableVertexAttribArray( VERTEX_ATTRIB_COLOR );

	glBindVertexArray( 0 );

	_pcMeshData = TYPED_ALLOC_ALIGNED( MESH_VERTEX, _uiVerticesCount, 16 );

	/*for	( uint i = 0; i < _uiVerticesCount; i++ )
	{
		MeshDataStream >> _pcMeshData[i]._vPosition;
		MeshDataStream >> _pcMeshData[i]._vColor;
	}*/
}

//-----------------------------------------------------------------------------
//! @brief		LoadObjFile
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	MESH::LoadObjFile(const char *	pObjFilePath)
{
	QFile objFile( pObjFilePath );

	if	( objFile.open( QIODevice::ReadOnly ) == false )
	{
		QMessageBox msgBox( QMessageBox::Critical, "Mesh loading error", "Error loading mesh at the following path: " + QString( pObjFilePath ), QMessageBox::Ok );
		msgBox.exec();

		return;
	}

	QTextStream				fileStream( &objFile );
	QString					strLine;
	QVector<VECTOR4>		vVertices;
	QVector<VECTOR4>		vNormals;
	QVector<POINT3>			vTexturesCoords;
	QVector<OBJ_FACE_POINT>	vUniquePoints;
	QVector<uint>			vElementIndices;
	QStringList				lTokens;

	fileStream.seek( 0 );

	while	( fileStream.atEnd() == false )
	{
		strLine = fileStream.readLine();

		if	( strLine.startsWith( "v " ) )
		{
			strLine.remove( 0, 2 );
			lTokens = strLine.split( ' ' );

			vVertices.push_back( VECTOR4( lTokens[0].toFloat(), lTokens[1].toFloat(), lTokens[2].toFloat(), 1.0f ) );
		}
		else if	( strLine.startsWith( "vt " ) )
		{
			strLine.remove( 0, 3 );
			lTokens = strLine.split( ' ' );

			vTexturesCoords.push_back( POINT3( lTokens[0].toFloat(), lTokens[1].toFloat(), 1.0f ) );
		}
		else if	( strLine.startsWith( "vn " ) )
		{
			strLine.remove( 0, 3 );
			lTokens = strLine.split( ' ' );

			vNormals.push_back( VECTOR4( lTokens[0].toFloat(), lTokens[1].toFloat(), lTokens[2].toFloat(), 0.0f ) );
		}
		else if	( strLine.startsWith( "f " ) )
		{
			strLine.remove( 0, 2 );
			lTokens = strLine.split( ' ' );

			QStringList lFacePointTokens;

			for	( uint i = 0; i < 3; i++ )
			{
				OBJ_FACE_POINT FacePoint;

				lFacePointTokens = lTokens[i].split( '/' );

				FacePoint._uiVertexIndex	= lFacePointTokens[0].toUInt();
				FacePoint._uiTexCoordIndex	= lFacePointTokens[1].toUInt();
				FacePoint._uiNormalIndex	= lFacePointTokens[2].toUInt();

				int nFacePointIndex = vUniquePoints.indexOf( FacePoint );

				if	( nFacePointIndex != -1 )
				{
					vElementIndices.push_back( nFacePointIndex );
				}
				else
				{
					vUniquePoints.push_back( FacePoint );
					vElementIndices.push_back( vUniquePoints.count() - 1 );
				}
			}
		}
	}

	_uiVerticesCount = vUniquePoints.count();
	_uiElementsCount = vElementIndices.count();

	_pcMeshData			= TYPED_ALLOC_ALIGNED( MESH_VERTEX, _uiVerticesCount, 16 );
	_puiElementIndices	= new uint[_uiElementsCount];

	for	( uint i = 0; i < _uiVerticesCount; i++ )
	{
		const OBJ_FACE_POINT& FacePoint = vUniquePoints[i];

		_pcMeshData[i]._vPosition	= vVertices[FacePoint._uiVertexIndex-1];
		_pcMeshData[i]._vColor		= VECTOR4( 1.0f, 0.0f, 0.0f, 1.0f );
		_pcMeshData[i]._vNormal		= vNormals[FacePoint._uiNormalIndex-1];
		_pcMeshData[i]._vTexCoord	= vTexturesCoords[FacePoint._uiTexCoordIndex-1];
	}

	memcpy( _puiElementIndices, vElementIndices.constData(), _uiElementsCount * sizeof(uint) );

	uint uiPositionDataOffset	= offsetof( MESH_VERTEX, _vPosition );
	uint uiColorDataOffset		= offsetof( MESH_VERTEX, _vColor );
	uint uiNormalDataOffset		= offsetof( MESH_VERTEX, _vNormal );

	glGenVertexArrays( 1, &_uiVertexArrayId );
	glBindVertexArray( _uiVertexArrayId );

	glGenBuffers( 1, &_uiVertexBufferId );
	glBindBuffer( GL_ARRAY_BUFFER, _uiVertexBufferId );
	glBufferData( GL_ARRAY_BUFFER, _uiVerticesCount * sizeof(MESH_VERTEX), _pcMeshData, GL_STATIC_DRAW );

	glGenBuffers( 1, &_uiElementArrayId );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _uiElementArrayId );
	glBufferData( GL_ELEMENT_ARRAY_BUFFER, _uiElementsCount * sizeof(uint), _puiElementIndices, GL_STATIC_DRAW );

	glVertexAttribPointer( VERTEX_ATTRIB_POSITION,	4, GL_FLOAT, GL_FALSE, sizeof(MESH_VERTEX), (GLvoid*)uiPositionDataOffset );
	glVertexAttribPointer( VERTEX_ATTRIB_COLOR,		4, GL_FLOAT, GL_FALSE, sizeof(MESH_VERTEX), (GLvoid*)uiColorDataOffset );
	glVertexAttribPointer( VERTEX_ATTRIB_NORMAL,	4, GL_FLOAT, GL_FALSE, sizeof(MESH_VERTEX), (GLvoid*)uiNormalDataOffset );

	glBindBuffer( GL_ARRAY_BUFFER, 0 );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
	glBindVertexArray( 0 );
}

//-----------------------------------------------------------------------------
//! @brief		Bind
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-26
//-----------------------------------------------------------------------------
void	MESH::Bind() const
{
	glBindVertexArray( _uiVertexArrayId );
	glBindBuffer( GL_ARRAY_BUFFER, _uiVertexBufferId );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _uiElementArrayId );
}

//-----------------------------------------------------------------------------
//! @brief		Unbind
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-26
//-----------------------------------------------------------------------------
void	MESH::Unbind() const
{
	glBindVertexArray( 0 );
	glBindBuffer( GL_ARRAY_BUFFER, 0 );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
}

//-----------------------------------------------------------------------------
//! @brief		Draw
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-26
//-----------------------------------------------------------------------------
void	MESH::Draw() const
{
	//glDrawArrays( GL_TRIANGLES, 0, _uiVerticesCount );
	glDrawElements( GL_TRIANGLES, _uiElementsCount, GL_UNSIGNED_INT, 0 );
}

//-----------------------------------------------------------------------------
//! @brief		GetMeshVertex
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-26
//-----------------------------------------------------------------------------
MESH_VERTEX*	MESH::GetMeshVertex( const uint uiIndex )
{
	return	( &_pcMeshData[uiIndex] );
}

//=============================================================================
//	CODE ENDS HERE
//=============================================================================
